// =======================================================================
// 
//       Filename:  plodder.cpp
// 
//    Description:  
// 
//        Version:  1.0
//        Created:  27.03.2010 23:16:24
//       Revision:  none
//       Compiler:  g++
// 
//         Author:  Dominik 'domachine' Burgdörfer (-), dominik.burgdoerfer@googlemail.com
//        Company:  -
// 
// =======================================================================

#include "plodder.hpp"
#include <string.h>
#include <vector>
#include <boost/regex.hpp>
#include <unistd.h>
#include <fstream>
#include "citerator.hpp"
#include <boost/filesystem.hpp>
#include <cerrno>
#include <boost/foreach.hpp>
#include "utils/comparenodes.hpp"

#define foreach BOOST_FOREACH

namespace fs = boost::filesystem;
using namespace std;
using namespace boost;
using webplodder::utils::compareNodes;

namespace webplodder {
    Plodder::Plodder(const string& configDir)
        : mconfigDir(configDir), 
          mCacheMode(Parser::CACHE_ALL)
    {

    }

    Plodder::~Plodder() {
    }

    void Plodder::scanDir(const string& dir) 
       
    {
        if(chdir(dir.c_str()) < 0) {
            perror("unable to change directory");
        }

        // Open the given directory and search it for
        // page files
        fs::recursive_directory_iterator dIter(".");
        fs::recursive_directory_iterator endIter;
        vector<Page> pageFiles;
        fs::path templateFile;
        regex pageFileRegex("^.+\\.page$");
        vector<navigation::Node> navi;

        for(; dIter != endIter; ++dIter) {
            if(fs::is_regular_file(dIter->status()) &&
                    dIter->path().extension() == ".page") {
                fs::path outFile(dIter->path());
                outFile.replace_extension("html");
                Page pageFile(dIter->path(), 
                        outFile);

                try {
                    // Parse the current page file ...
                    pageFile.parse();
                }
                catch(YAML::Exception& e) {
                    cerr << pageFile.sourcePath() << ": error: "
                        << e.what() << endl;
                    return;
                }

                if(pageFile["in_menu"].empty())
                    pageFile["in_menu"] = "1";
                

                int pos;
                if(! pageFile["pos"].empty())
                    pos = atoi(pageFile["pos"].c_str());

                if(pageFile["in_menu"] == "1")
                    navi.push_back(navigation::Node(pageFile["title"],
                                        pageFile.outputPath().string(),
                                        pos));

                pageFiles.push_back(pageFile);
            }
            else if(fs::is_regular_file(dIter->status()) &&
                    dIter->path().filename() == "default.template")
            {
                templateFile = dIter->path();
            }
        }

        if(templateFile.empty())
            throw runtime_error("No template file found");

        if(pageFiles.empty())
            throw runtime_error("No page files found");

        // Sort navigation
        sort(navi.begin(),
                navi.end(), compareNodes);

        // Install all navigations
        foreach(Page& file, pageFiles)
            file.installNavigation(navi);

        // Construct a Template::File object
        // upon the source file and parse it
        Template::File cTemplateFile(templateFile);
        Parser parser;

        parser.setCacheMode(mCacheMode);

        try {
            parser.parse(cTemplateFile);
        }
        catch(SyntaxError& e) {
            cerr << templateFile.filename()
                << ":" << e.line() << ": error: "
                << e.what() << endl;
            return;
        }

        // Iterate through the page queue
        foreach(Page pageFile, pageFiles)
        {
            // ... and pass it to the Processor
            mProcessor->process(cTemplateFile, pageFile);
        }
    }

    void Plodder::init()
    {
        // Strip a possibly existing slash at the end
        if(*(mconfigDir.end()) == '/')
            mconfigDir.erase(mconfigDir.end());

        string configFile = mconfigDir + "/config.yaml";

        // Open config file and parse it
        ifstream in(configFile.c_str());
        if(in.good()) {
            YAML::Parser parser(in);

            YAML::Node doc;

            parser.GetNextDocument(doc);
            YAML::Iterator it;

            string key;

            for(it = doc.begin(); it != doc.end(); ++it) {
                it.first() >> key;
                it.second() >> mscriptConfig[key];
            }

            in.close();
        }

        // Pass the parsed config to the Processor
        mProcessor = shared_ptr<Processor>(
                new Processor(mscriptConfig));
        scanPlugins();
    }

    void Plodder::scanPlugins()
    {
        fs::directory_iterator endIter;
        fs::path pluginPath(mconfigDir + "/plugins");

        if(!fs::is_directory(pluginPath)) {
            cerr << "info: Unable to scan plugins" << endl;
            return;
        }

        fs::directory_iterator dIter(pluginPath);

        Plugin* plugin;
        for(; dIter != endIter; ++dIter) {
            if(dIter->path().extension() == ".so") {
                plugin = new Plugin(dIter->path());
                plugin->open();
                //plugin->close();
                mProcessor->installPlugin(shared_ptr<Plugin>(plugin));
            }
        }
    }

    void Plodder::setCacheMode(Parser::CacheMode cacheMode)
    {
        mCacheMode = cacheMode;
    }
}
